package com.me.batm.views;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.FPSLogger;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Animation;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.ParticleEffect;
import com.badlogic.gdx.graphics.g2d.ParticleEffectPool;
import com.badlogic.gdx.graphics.g2d.ParticleEffectPool.PooledEffect;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.graphics.g2d.tiled.TileMapRenderer;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Array;
import com.me.batm.GameConstants;
import com.me.batm.GameConstants.Direction;
import com.me.batm.models.Aura;
import com.me.batm.models.GameEntity;
import com.me.batm.models.GameWorld;
import com.me.batm.models.InverseAura;
import com.me.batm.models.LifeAura;
import com.me.batm.models.LifeBullet;
import com.me.batm.models.Player;
import com.me.batm.models.SpeedAura;
import com.me.batm.models.Target;
import com.me.batm.models.TeleportAura;
import com.me.batm.models.TimedBlur;
import com.me.batm.models.TimedControlReverse;
import com.me.batm.models.TimedSpeed;
import com.me.batm.services.GameConfiguration;

public class GameRenderer {
	private static final String TAG = GameRenderer.class.getSimpleName();

	private GameWorld gameWorld;
	private TileMapRenderer tileMapRenderer;
	private OrthographicCamera camera;
	private ShapeRenderer shapeRenderer = new ShapeRenderer();
	private SpriteBatch spriteBatch = new SpriteBatch();
	private FPSLogger fpsLogger = new FPSLogger();

	// Life
	private float lifeUnit;
	private float lifeBar = 100f;
	private Collection<BloodParticle> bloodParticles;
	private Texture lifeBarBGTex, lifeBarTex;
	private TextureRegion lifeBarTexRegion;
	private Texture playerVision;
	private Texture smallPlayerVision;
	/**************************************************/
	/******************** SPRITES *********************/
	/**************************************************/

	// Guy
	private Animation guyWalkLeft;
	private Animation guyWalkRight;
	private Animation guyWalkUp;
	private Animation guyWalkDown;

	// Girl
	private Animation girlWalkLeft;
	private Animation girlWalkRight;
	private Animation girlWalkUp;
	private Animation girlWalkDown;

	// Aura
	private Texture slowstone;
	private Texture teleportstone;
	private Texture confusedstone;
	private Texture lifestone;
	private Texture proyectil1;
	private Texture proyectil2;
	private Texture proyectil3;
	private Texture proyectil4;
	
	//Memes :)
	private TextureRegion rageMeme;
	private TextureRegion okayMeme;
	private TextureRegion foreverAloneMeme;
	//private TextureRegion bulletTime;

	ParticleEffectPool bombEffectPool;
	Array<PooledEffect> effects = new Array();

	private BitmapFont simpleFont;

	private void loadTextures() {
		lifeBarBGTex = new Texture(Gdx.files.internal("LifeBarBG.png"));
		lifeBarTex = new Texture(Gdx.files.internal("LifeBar.png"));

		guyWalkUp = loadTextureRegion(
				Gdx.files.internal("sprites/guy_walk_up.png"),
				GameConstants.GUY_WALK_UP_SPRITES, false);
		guyWalkDown = loadTextureRegion(
				Gdx.files.internal("sprites/guy_walk_down.png"),
				GameConstants.GUY_WALK_DOWN_SPRITES, false);
		guyWalkLeft = loadTextureRegion(
				Gdx.files.internal("sprites/guy_walk_left.png"),
				GameConstants.GUY_WALK_SIDE_SPRITES, false);
		guyWalkRight = loadTextureRegion(
				Gdx.files.internal("sprites/guy_walk_right.png"),
				GameConstants.GUY_WALK_SIDE_SPRITES, false);

		girlWalkLeft = loadTextureRegion(
				Gdx.files.internal("sprites/girl_walk_left.png"),
				GameConstants.GIRL_WALK_SIDE_SPRITES, false);
		girlWalkRight = loadTextureRegion(
				Gdx.files.internal("sprites/girl_walk_right.png"),
				GameConstants.GIRL_WALK_SIDE_SPRITES, false);
		girlWalkUp = loadTextureRegion(
				Gdx.files.internal("sprites/girl_walk_up.png"),
				GameConstants.GIRL_WALK_UP_SPRITES, false);
		girlWalkDown = loadTextureRegion(
				Gdx.files.internal("sprites/girl_walk_down.png"),
				GameConstants.GIRL_WALK_DOWN_SPRITES, false);
		
		//memes
		rageMeme = new TextureRegion(new Texture(Gdx.files.internal("sprites/guy_rage_down.png")));
		okayMeme = new TextureRegion(new Texture(Gdx.files.internal("sprites/guy_okay_down.png")));
		foreverAloneMeme = new TextureRegion(new Texture(Gdx.files.internal("sprites/guy_foreveralone_down.png")));
		//bulletTime = new TextureRegion(new Texture(Gdx.files.internal("sprites/guybullettime.png")));

		// Aura
		slowstone = new Texture(Gdx.files.internal("slowstone.png"));
		confusedstone = new Texture(Gdx.files.internal("confusedstone.png"));
		teleportstone = new Texture(Gdx.files.internal("teleportstone.png"));
		lifestone = new Texture(Gdx.files.internal("stonelife.png"));
		proyectil1 = new Texture(Gdx.files.internal("proyectil1.png"));
		proyectil2 = new Texture(Gdx.files.internal("proyectil2.png"));
		proyectil3 = new Texture(Gdx.files.internal("proyectil3.png"));
		proyectil4 = new Texture(Gdx.files.internal("proyectil4.png"));
		// Aura Particles
		ParticleEffect bombEffect = new ParticleEffect();
		// bombEffect.load(Gdx.files.internal("data/fire.p"),
		// Gdx.files.internal("sprites"));
		bombEffect.load(Gdx.files.internal("data/fire.p"), Gdx.files.internal("sprites"));
//		bombEffect.load(Gdx.files.internal("data/bubble.p"), Gdx.files.internal("sprites"));
		bombEffectPool = new ParticleEffectPool(bombEffect, 1, 2);
		PooledEffect effect = bombEffectPool.obtain();
		effects.add(effect);

		playerVision = new Texture(Gdx.files.internal("sprites/vision.png"));
		smallPlayerVision = new Texture(
				Gdx.files.internal("sprites/smallvision.png"));
	}

	private Animation loadTextureRegion(FileHandle texturePath,
			int spriteCount, boolean flipX) {
		return loadTextureRegion(texturePath, spriteCount,
				GameConstants.COMMON_KEYFRAME_DURATION, flipX);
	}

	private Animation loadTextureRegion(FileHandle texturePath,
			int spriteCount, float frameDuration, boolean flipX) {
		Texture texture = new Texture(texturePath);
		TextureRegion textureRegions[][] = TextureRegion.split(texture,
				texture.getWidth() / spriteCount, texture.getHeight());

		Array<TextureRegion> keyFrames = new Array<TextureRegion>();
		for (int i = 0; i < textureRegions[0].length; i++) {
			if (flipX)
				textureRegions[0][i].flip(true, false);
			keyFrames.add(textureRegions[0][i]);
		}

		Animation animation = new Animation(frameDuration, keyFrames);

		animation.setPlayMode(Animation.LOOP);

		return animation;
	}

	/**************************************************/
	/****************** END SPRITES *******************/
	/**************************************************/

	public GameRenderer(GameWorld world) {
		loadResources();

		camera = new OrthographicCamera(GameConstants.SCREEN_WIDTH,
				GameConstants.SCREEN_HEIGHT);
		camera.position.x = GameConstants.SCREEN_WIDTH / 2f;
		camera.position.y = GameConstants.SCREEN_HEIGHT / 2f;
		camera.update();
		this.gameWorld = world;
		tileMapRenderer = new TileMapRenderer(world.tiledMap, world.tileAtlas,
				16, 16);

		bloodParticles = new ArrayList<BloodParticle>();
	}

	public void loadResources() {
		loadTextures();
		loadSounds();

		simpleFont = new BitmapFont();
	}

	private void loadSounds() {
	}

	public void resize(float width, float height) {
	}

	public void setGameWorld(GameWorld gareveWorld) {
		this.gameWorld = gareveWorld;
	}

	public void render() {
		initCamera(gameWorld.player);
		
		renderMap();		
		renderEntities(gameWorld.entities);
		renderTarget(gameWorld.target);
		renderPlayer(gameWorld.player);
		renderPlayerVision(gameWorld.player);
		renderLifePlayer(gameWorld.player);
		
		//renderAngerConquerInfo(gameWorld.target.angerBar,gameWorld.target.conquerBar);
		renderStageInfo();
	}
	
	private void renderStageInfo(){
		String message = String.format("Stage = %d / %s",GameConfiguration.getActualLevel(),GameConfiguration.getDifficulty());
		Gdx.app.log(TAG, message);
		spriteBatch.begin();
			simpleFont.draw(spriteBatch,message, gameWorld.player.getCenterPosition().x
					- GameConstants.SCREEN_WIDTH / 2.1f,
			gameWorld.player.getCenterPosition().y
					- GameConstants.SCREEN_HEIGHT * 0.45f);
		spriteBatch.end();
	}

	private void renderAngerConquerInfo(float anger, float conquer) {
		spriteBatch.begin();
		simpleFont.draw(spriteBatch,
				String.format("Anger = %.2f / Conquer = %.2f", anger, conquer),
				gameWorld.player.getCenterPosition().x
						- GameConstants.SCREEN_WIDTH / 2f,
				gameWorld.player.getCenterPosition().y
						- GameConstants.SCREEN_HEIGHT * 0.45f);
		spriteBatch.end();
	}

	private void renderPlayerVision(Player player) {
		// spriteBatch.enableBlending();
		spriteBatch.begin();
		spriteBatch.draw(getPlayerVisionTexture(), player.getCenterPosition().x
				- GameConstants.SCREEN_WIDTH / 2f, player.getCenterPosition().y
				- GameConstants.SCREEN_HEIGHT / 2f, GameConstants.SCREEN_WIDTH,
				GameConstants.SCREEN_HEIGHT);
		spriteBatch.end();
		// spriteBatch.disableBlending();
	}
	
	private Texture getPlayerVisionTexture(){
		if(GameConfiguration.getActualLevel() == 3 || gameWorld.inputStatus.isSpace || gameWorld.player.blurryView)
			return smallPlayerVision;
		return playerVision;
	}

	private void renderEntities(Array<GameEntity> entities) {
		// TODO Auto-generated method stub
		GameEntity entity;
		Aura aura;
		
		shapeRenderer.begin(ShapeType.Rectangle);
		for (Iterator<GameEntity> iter = entities.iterator(); iter.hasNext();) {
			entity = iter.next();
			if (entity.render) {
				
				//shapeRenderer.rect(entity.getX(), entity.getY(), entity.WIDTH,
				//		entity.HEIGHT);

			} else {
				iter.remove();
				System.out.println(entities.size);
			}
		}
		shapeRenderer.end();

		spriteBatch.begin();
		for (int j = 0; j < entities.size; j += 1) {
			entity = entities.get(j);
			if (entity instanceof SpeedAura) {
				spriteBatch.draw(slowstone, entity.getX(), entity.getY(),
						entity.getWidth(), entity.getHeight());
			} else if (entity instanceof TeleportAura) {
				spriteBatch.draw(teleportstone, entity.getX(), entity.getY(),
						entity.getWidth(), entity.getHeight());
			} else if (entity instanceof InverseAura) {
				spriteBatch.draw(confusedstone, entity.getX(), entity.getY(),
						entity.getWidth(), entity.getHeight());
			} else if (entity instanceof TimedControlReverse) {
				spriteBatch.draw(proyectil2, entity.getX(), entity.getY(),
						entity.getWidth(), entity.getHeight());
			} else if (entity instanceof LifeAura) {
				spriteBatch.draw(lifestone, entity.getX(), entity.getY(),
						entity.getWidth(), entity.getHeight());
			} else if (entity instanceof TimedControlReverse) {
				spriteBatch.draw(proyectil1, entity.getX(), entity.getY(),
						entity.getWidth(), entity.getHeight());
			} else if (entity instanceof TimedSpeed) {
				spriteBatch.draw(proyectil2, entity.getX(), entity.getY(),
						entity.getWidth(), entity.getHeight());
			} else if (entity instanceof LifeBullet) {
				spriteBatch.draw(proyectil3,entity.getX(), entity.getY(),
						entity.getWidth(), entity.getHeight());
			} else if (entity instanceof TimedBlur) {
				spriteBatch.draw(proyectil4,entity.getX(), entity.getY(),
						entity.getWidth(), entity.getHeight());
			}
		}

		for (int j = 0; j < entities.size; j += 1) {
			entity = entities.get(j);
			if(entity instanceof SpeedAura || entity instanceof TeleportAura || entity instanceof InverseAura || entity instanceof LifeAura) {
				for (int i = effects.size - 1; i >= 0; i--) {
	//				shapeRenderer.circle(aura.getX(), aura.getY(), aura.getRadio());
					PooledEffect effect = effects.get(i);
			        effect.draw(spriteBatch, Gdx.graphics.getDeltaTime());
			        
			        float posX = (float) (((Math.random() * 1000)) % (entity.WIDTH));
			        float posY = (float) (((Math.random() * 1000)) % (entity.HEIGHT));
			        effect.setPosition(entity.getX() + posX, entity.getY() + posY);
			        
			        if (effect.isComplete()) {
						effect.free();
						effects.removeIndex(i);
			        }
				}
			}
		}
		spriteBatch.end();
	}

	public void renderMap() {

		int i;
		tileMapRenderer.render(camera);
		shapeRenderer.begin(ShapeType.Rectangle);
		Rectangle rect;
		/*
		 * shapeRenderer.line(player.p1.x,player.p1.y,player.p2.x,player.p2.y);
		 * shapeRenderer.line(player.p2.x,player.p2.y,player.p3.x,player.p3.y);
		 * shapeRenderer.line(player.p3.x,player.p3.y,player.p4.x,player.p4.y);
		 * shapeRenderer.line(player.p4.x,player.p4.y,player.p1.x,player.p1.y);
		 */
		for (i = 0; i < gameWorld.mapRectangles.size; i += 1) {
			rect = gameWorld.mapRectangles.get(i);
			//shapeRenderer.rect(rect.x, rect.y, rect.width, rect.height);
		}
		shapeRenderer.end();

	}

	private void renderLifePlayer(Player player) {
		Gdx.gl.glEnable(GL10.GL_BLEND);
		float x = player.getCenterPosition().x - GameConstants.SCREEN_WIDTH
				* 0.45f;
		float widthLife = (GameConstants.SCREEN_WIDTH * 0.462f)
				* (player.life / 100f);
		float y = player.getCenterPosition().y + GameConstants.SCREEN_HEIGHT
				* 0.33f;

		spriteBatch.begin();
		spriteBatch.draw(lifeBarBGTex, x, y, GameConstants.SCREEN_WIDTH * 0.5f,
				GameConstants.SCREEN_HEIGHT * 0.15f);
		x += GameConstants.SCREEN_WIDTH * 0.015f;
		y += GameConstants.SCREEN_HEIGHT * 0.047f;
		lifeBarTexRegion = new TextureRegion(lifeBarTex, 0, 0, 736, 114);
		spriteBatch.draw(lifeBarTexRegion, x, y, widthLife,
				GameConstants.SCREEN_HEIGHT * 0.10f);
		spriteBatch.end();

		shapeRenderer.begin(ShapeType.FilledRectangle);
		shapeRenderer.setColor(0.9f, 0f, 0f, 1f);
		// shapeRenderer.filledRect(x, y, widthLife,
		// GameConstants.SCREEN_HEIGHT*0.055f); //Life Bar
		float pSize = GameConstants.SCREEN_HEIGHT * (BloodParticle.SIZE / 100f);
		for (Iterator<BloodParticle> iterator = bloodParticles.iterator(); iterator
				.hasNext();) {
			BloodParticle particle = iterator.next();
			if (!particle.isAlive()) {
				iterator.remove();
				continue;
			}
			shapeRenderer.setColor(0.9f, 0f, 0f, particle.alpha);
			shapeRenderer.filledRect(particle.position.x, particle.position.y,
					pSize, pSize);
			particle.update(0.01f);
		}
		shapeRenderer.setColor(0f, 1f, 0f, 1f); // Reset Alpha
		shapeRenderer.end();
		Gdx.gl.glDisable(GL10.GL_BLEND);
		if (player.getBloodLost()) {
			float xRandom = (((int) (Math.random() * 1000f) % 200) - 100f) / 100f;
			float yRandom = ((int) (Math.random() * 1000f) % 400) + 300f;
			yRandom = y + GameConstants.SCREEN_HEIGHT * (yRandom / 10000f);
			bloodParticles.add(new BloodParticle(x + widthLife, yRandom,
					new Vector2(xRandom, -1f)));
		}
	}

	private void renderPlayer(Player player) {
		//shapeRenderer.begin(ShapeType.Rectangle);
		/*
		 * shapeRenderer.line(player.p1.x,player.p1.y,player.p2.x,player.p2.y);
		 * shapeRenderer.line(player.p2.x,player.p2.y,player.p3.x,player.p3.y);
		 * shapeRenderer.line(player.p3.x,player.p3.y,player.p4.x,player.p4.y);
		 * shapeRenderer.line(player.p4.x,player.p4.y,player.p1.x,player.p1.y);
		 */
		//shapeRenderer.rect(player.shape.x, player.shape.y, player.shape.width,
		//		player.shape.height);
		//shapeRenderer.end();

		spriteBatch.begin();
		spriteBatch.draw(getPlayerAnimation(player), player.shape.x,
				player.shape.y, player.shape.width, player.shape.height);
		spriteBatch.end();
	}

	private TextureRegion getPlayerAnimation(Player player) {
		if(gameWorld.target.teletransportTime <= 1f)
			return foreverAloneMeme;
		if(player.teletransportTime <= 1f)
			return rageMeme;
		
		//if(gameWorld.inputStatus.isSpace)
		//	return bulletTime;
		
		if (player.getDirection() == Direction.DOWN)
			return guyWalkDown.getKeyFrame(player.keyFrameTime);
		if (player.getDirection() == Direction.UP)
			return guyWalkUp.getKeyFrame(player.keyFrameTime);
		if (player.getDirection() == Direction.LEFT)
			return guyWalkLeft.getKeyFrame(player.keyFrameTime);
		if (player.getDirection() == Direction.RIGHT)
			return guyWalkRight.getKeyFrame(player.keyFrameTime);
		throw new RuntimeException("Invalid player Direction");
	}

	private void renderTarget(Target target) {
		//shapeRenderer.begin(ShapeType.Rectangle);
		//shapeRenderer.rect(target.shape.x, target.shape.y, target.shape.width,
		//		target.shape.height);
		//shapeRenderer.end();
		/*
		shapeRenderer.begin(ShapeType.Circle);
		shapeRenderer.circle(target.getCenterPosition().x,
				target.getCenterPosition().y,
				GameConstants.MINIMUM_CONQUER_RADIO_AFFECT, 20);
		shapeRenderer.circle(target.getCenterPosition().x,
				target.getCenterPosition().y,
				GameConstants.MINIMUM_ANGER_RADIO_AFFECT, 20);
		shapeRenderer.end();
		*/

		spriteBatch.begin();
		spriteBatch.draw(getTargetAnimation(target), target.shape.x,
				target.shape.y, target.shape.width, target.shape.height);
		spriteBatch.end();

		shapeRenderer.begin(ShapeType.FilledRectangle);
		shapeRenderer.setColor(0f, 0.9f, 0f, 1f);
		float x = target.getCenterPosition().x - target.shape.width;
		float y = target.getCenterPosition().y + target.shape.height * 0.65f;
		float widthConquerBar = target.shape.width * 2
				* (target.conquerBar / 100f);
		float widthAngerBar = target.shape.width * 2 * (target.angerBar / 100f);
		shapeRenderer.filledRect(x, y, widthConquerBar,
				GameConstants.SCREEN_HEIGHT * 0.01f); // Conquer Bar
		shapeRenderer.setColor(1f, 0.5f, 0.0f, 1f);
		shapeRenderer.filledRect(x, y + GameConstants.SCREEN_HEIGHT * 0.015f,
				widthAngerBar, GameConstants.SCREEN_HEIGHT * 0.01f); // Anger
																		// Bar
		shapeRenderer.end();
	}

	private TextureRegion getTargetAnimation(Target target) {
		if (target.getDirection() == Direction.DOWN)
			return girlWalkDown.getKeyFrame(target.keyFrameTime);
		if (target.getDirection() == Direction.UP)
			return girlWalkUp.getKeyFrame(target.keyFrameTime);
		if (target.getDirection() == Direction.LEFT)
			return girlWalkLeft.getKeyFrame(target.keyFrameTime);
		if (target.getDirection() == Direction.RIGHT)
			return girlWalkRight.getKeyFrame(target.keyFrameTime);
		throw new RuntimeException("Invalid Target Direction");
	}

	public void initCamera(Player player) {
		camera.position.x = gameWorld.player.center.x;
		camera.position.y = gameWorld.player.center.y;
		camera.update();

		shapeRenderer.setProjectionMatrix(camera.combined);
		spriteBatch
				.getProjectionMatrix()
				.setToOrtho2D(
						player.getCenterPosition().x
								- GameConstants.SCREEN_WIDTH / 2f,
						player.getCenterPosition().y
								- GameConstants.SCREEN_HEIGHT / 2f,
						GameConstants.SCREEN_WIDTH, GameConstants.SCREEN_HEIGHT);

		Gdx.gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
		Gdx.gl.glClearColor(0.1f, 0.1f, 0.1f, 1);
		Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);

		Gdx.gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);

		fpsLogger.log();
	}
	
	public void printStartLevelScreen(int level,String difficulty){
		/*
		Gdx.gl.glClearColor(0.0f, 0.0f, 0.0f, 1);
		Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
		
		spriteBatch.begin();
			simpleFont.draw(spriteBatch, String.format("Stage %d | Level %s",level,difficulty), gameWorld.player.getCenterPosition().x - GameConstants.SCREEN_WIDTH * 0.2f, gameWorld.player.getCenterPosition().y);
		spriteBatch.end();
		*/
	}

	public void dispose() {
		tileMapRenderer.dispose();
		shapeRenderer.dispose();
		spriteBatch.dispose();

		bloodParticles.clear();
		lifeBarTex.dispose();
		lifeBarBGTex.dispose();
		playerVision.dispose();		

		simpleFont.dispose();
	}
}
